package com.tianji.learning.service.impl;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constant.RedisConstant;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.message.SignInMessage;
import com.tianji.learning.service.SignService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.swing.text.DateFormatter;
import java.text.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class SignServiceImpl implements SignService {

    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper rabbitMqHelper;

    @Override
    public SignResultVO signRecord() throws ParseException {
        //1.获取当前用户id
        Long userId = UserContext.getUser();
        //2.去redis中查询是否已经签到过
        //获取当前日期
        DateFormat dateFormat = new SimpleDateFormat("yyyyMM");
        DateFormatter dateFormatter = new DateFormatter(dateFormat);
        LocalDateTime now = LocalDateTime.now();
        //格式化日期
        String format = dateFormatter.valueToString(now);
        //创建key
        String key = RedisConstant.SIGN_RECORD_KEY_PREFIX + userId + format;
        //获取偏移值
        int dayOfMonth = now.getDayOfMonth() - 1;
        Boolean b = redisTemplate.opsForValue().setBit(key, dayOfMonth, true);
        if (b != null && b) {
            //已经签到过
            throw new BizIllegalException("不能重复签到!");
        }
        //3.如果没签到过,那么上面就已经完成了签到过程,现在需要计算积分
        //3.1查询本月到今天为止的签到记录
        int days = countSignDays(key, dayOfMonth + 1);
        //3.2计算积分
        Integer rewardPoints = 0;
        switch (days) {
            case 7:
                rewardPoints = 10;
                break;
            case 14:
                rewardPoints = 20;
                break;
            case 28:
                rewardPoints = 30;
                break;
        }
        //发送mq消息到积分服务队列
        rabbitMqHelper.send(
                MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                SignInMessage.of(userId, rewardPoints + 1)
        );
        //3.3封装VO
        SignResultVO signResultVO = new SignResultVO();
        signResultVO.setSignDays(days);
        signResultVO.setRewardPoints(rewardPoints);
        signResultVO.setSignPoints(1);
        return signResultVO;
    }

    @Override
    public List<Integer> signRecords() {
        //1.获取当前用户
        Long user = UserContext.getUser();
        int dayOfMonth = LocalDateTime.now().getDayOfMonth();
        //2.去redis中获取当前用户本月从开始到现在的签到记录
        String key = RedisConstant.SIGN_RECORD_KEY_PREFIX + user;
        List<Long> longs = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands
                .create()
                .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))
                .valueAt(0)); //返回的是一串二进制转化后的十进制数
        if(CollUtils.isEmpty(longs)){
            return new ArrayList<>();
        }
        Long l = longs.get(0);
        //3.转化成数字组成的数组返回去
        LinkedList<Integer> result = new LinkedList<>();
        for (int i = 0; i < dayOfMonth; i++) {
            if ((l & 1) == 1) {
                result.addFirst(1);
            } else {
                result.addFirst(0);
            }
            l = l >>> 1; //将数字右移一位继续进行与1的与操作
        }
        return result;
    }

    private int countSignDays(String key, int i) {
        List<Long> longs = redisTemplate.opsForValue().bitField(key,
                BitFieldSubCommands.create().
                        get(BitFieldSubCommands.BitFieldType.unsigned(i))
                        .valueAt(0));
        if (longs == null) {
            return 0;
        }
        //获取bitmap返回的十进制数字
        Long l = longs.get(0);
        int days = 0;
        while ((l & 1) == 1) {
            days++;
            l = l >>> 1; //将数字右移一位继续进行与1的与操作
        }
        return days;
    }
}
